package it.uniroma2.art.coda.extension;

import it.uniroma2.art.coda.exception.FelixInitializationException;
import it.uniroma2.art.coda.interfaces.IdentityResolverInterface;
import it.uniroma2.art.coda.interfaces.ParserPR;
import it.uniroma2.art.coda.interfaces.ProjectionRulesDeciderInterface;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.felix.framework.Felix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Andrea Turbati
 */

public class CODAOSGiManger extends ARTOSGiManager {

	private static CODAOSGiManger manager = null;
	protected static Logger logger = LoggerFactory
			.getLogger(CODAOSGiManger.class);

	private static final String projRuleParserPath = "ProjRuleParser";

	private static final String prjDisPath = "PrjRulDec";
	private static final String idResPath = "IdRes";
	private static final String resSmartSuggPath = "ResSmartSugg";
	private static final String triSmartSuggPath = "TriSmartSugg";

	private static String filePropertyName = "osgi.properties";
	private static String propertyName = "art.coda.osgi.packages";

	protected CODAOSGiManger(File felixDir) throws FelixInitializationException {
		super(felixDir, filePropertyName, propertyName);
	}

	/**
	 * The layout of the CODA components directory.
	 */
	public static enum Layout {
		/**
		 * All components in the root directory
		 */
		Flat,

		/**
		 * Each component under a directory structure as follows:
		 * <code>rootDir/{component type}/{component dir}<code>
		 */
		Folded,

		/**
		 * Each component under a directory structure as follows:
		 * <code>rootDir/{a dir}/{component type}/{component dir}<code>
		 */
		DoubleFolded
	};

	/**
	 * Method to inialize the OSGi framwork using felix implementation
	 * 
	 * @param oSGiDir
	 *            the directory where the bundle will be stored
	 * @return the CODAOSGiManger
	 * @throws FelixInitializationException
	 */
	public static CODAOSGiManger initialize(File oSGiDir)
			throws FelixInitializationException {
		manager = new CODAOSGiManger(oSGiDir);
		return manager;
	}

	/**
	 * Get the CODAOSGiManger
	 * 
	 * @return the CODAOSGiManger
	 */
	public static CODAOSGiManger getManager() {
		return manager;
	}

	/**
	 * Stop Felix
	 */
	public void stop() {
		stopFelix();
	}

	/**
	 * Search, load and install all the bundles regarding the Projection Rules
	 * Parser Annotation module
	 * 
	 * @param rootDir
	 *            the directory where to start looking for all the bundles
	 *            regarding Projection Rule Parser
	 * @param subDirPresent
	 *            true if there are subdirectories in the rootDirectory, false
	 *            otherwise
	 */
	public void loadProjectionRulesParser(File rootDir, boolean subDirPresent) {
		bundleToBeStarted.clear();
		if (subDirPresent) {
			File[] subDirs = rootDir.listFiles();
			if (subDirs == null)
				return;
			for (File subDir : subDirs) {
				File[] resourceDirs = (new File(subDir, projRuleParserPath))
						.listFiles();
				if (resourceDirs == null)
					return;
				for (File resourceDir : resourceDirs) {
					findAndInstallPlugin(resourceDir);
				}
			}
		} else {
			File[] resourceDirs = (new File(rootDir, projRuleParserPath)
					.listFiles());
			if (resourceDirs == null)
				return;
			for (File resourceDir : resourceDirs) {
				findAndInstallPlugin(resourceDir);
			}
		}
		// Start all the bundle just found
		startAllBundle();
	}

	/**
	 * Search, load and install all the bundles regarding the Projection Rules
	 * Decider module
	 * 
	 * @param rootDir
	 *            the directory where to start looking for all the bundles
	 *            regarding the Projection Disambiguation Annotation
	 * @param subDirPresent
	 *            true if there are subdirectories in the rootDirectory, false
	 *            otherwise
	 * 
	 */
	public void loadProjectionRulesDecider(String rootDir, boolean subDirPresent) {
		bundleToBeStarted.clear();
		if (subDirPresent) {
			File[] subDirs = (new File(rootDir)).listFiles();
			if (subDirs == null)
				return;
			for (File subDir : subDirs) {
				File[] resourceDirs = (new File(subDir, prjDisPath))
						.listFiles();
				if (resourceDirs == null)
					continue;
				for (File resourceDir : resourceDirs) {
					findAndInstallPlugin(resourceDir);
				}
			}
		} else {
			File[] resourceDirs = (new File(rootDir, prjDisPath).listFiles());
			if (resourceDirs == null)
				return;
			for (File resourceDir : resourceDirs) {
				findAndInstallPlugin(resourceDir);
			}
		}
		// Start all the bundle just found
		startAllBundle();
	}

	/**
	 * Search, load and install all the bundles regarding the Identity Resolver
	 * module
	 * 
	 * @param rootDir
	 *            the directory where to start looking for all the bundles
	 *            regarding the Identity Resolution Annotation
	 * @param subDirPresent
	 *            true if there are subdirectories in the rootDirectory, false
	 *            otherwise
	 * 
	 */
	public void loadIdentityResolver(String rootDir, boolean subDirPresent) {
		bundleToBeStarted.clear();
		if (subDirPresent) {
			File[] subDirs = (new File(rootDir)).listFiles();
			if (subDirs == null)
				return;
			for (File subDir : subDirs) {
				File[] resourceDirs = (new File(subDir, idResPath)).listFiles();
				if (resourceDirs == null)
					return;
				for (File resourceDir : resourceDirs) {
					findAndInstallPlugin(resourceDir);
				}
			}
		} else {
			File[] resourceDirs = (new File(rootDir, idResPath).listFiles());
			if (resourceDirs == null)
				return;
			for (File resourceDir : resourceDirs) {
				findAndInstallPlugin(resourceDir);
			}
		}
		// Start all the bundle just found
		startAllBundle();
	}

	/**
	 * This method search, load and install all the bundles regarding the
	 * Resources Smart Suggestion module
	 * 
	 * @param rootDir
	 *            the directory where to start looking for all the bundles
	 *            regarding the Resouces Smart Suggestion
	 * @param subDirPresent
	 *            true if there are subdirectories in the rootDirectory, false
	 *            otherwise
	 * 
	 */
	public void loadResourcesSmartSuggestion(String rootDir,
			boolean subDirPresent) {
		bundleToBeStarted.clear();
		if (subDirPresent) {
			File[] subDirs = (new File(rootDir)).listFiles();
			if (subDirs == null)
				return;
			for (File subDir : subDirs) {
				File[] resourceDirs = (new File(subDir, resSmartSuggPath))
						.listFiles();
				if (resourceDirs == null)
					continue;
				for (File resourceDir : resourceDirs) {
					findAndInstallPlugin(resourceDir);
				}
			}
		} else {
			File[] resourceDirs = (new File(rootDir, resSmartSuggPath)
					.listFiles());
			if (resourceDirs == null)
				return;
			for (File resourceDir : resourceDirs) {
				findAndInstallPlugin(resourceDir);
			}
		}
		// Start all the bundle just found
		startAllBundle();
	}

	/**
	 * This method search, load and install all the bundles regarding the
	 * Triples Smart Suggestion module
	 * 
	 * @param rootDir
	 *            the directory where to start looking for all the bundles
	 *            regarding the Smart Suggestion
	 * @param subDirPresent
	 *            true if there are subdirectories in the rootDirectory, false
	 *            otherwise
	 * 
	 */
	public void loadTriplesSmartSuggestion(String rootDir, boolean subDirPresent) {
		bundleToBeStarted.clear();
		if (subDirPresent) {
			File[] subDirs = (new File(rootDir)).listFiles();
			if (subDirs == null)
				return;
			for (File subDir : subDirs) {
				File[] resourceDirs = (new File(subDir, triSmartSuggPath))
						.listFiles();
				if (resourceDirs == null)
					continue;
				for (File resourceDir : resourceDirs) {
					findAndInstallPlugin(resourceDir);
				}
			}
		} else {
			File[] resourceDirs = (new File(rootDir, triSmartSuggPath)
					.listFiles());
			if (resourceDirs == null)
				return;
			for (File resourceDir : resourceDirs) {
				findAndInstallPlugin(resourceDir);
			}
		}
		// Start all the bundle just found
		startAllBundle();
	}

	/**
	 * Finds and installs all bundles below <code>rootDir</code> The parameter
	 * <code>layout</code> specifies where to look for bundles (directly in the
	 * <code>rootDir</code>, or within sub-directories, ...)
	 * 
	 * @param bundlesRootDir
	 *            the directory to start the search of bundles from
	 * @param layout
	 *            the directory layout
	 */
	public void loadComponents(File bundlesRootDir, Layout layout) {
		if (layout != CODAOSGiManger.Layout.Flat) {
			// For a structured directory, fall back to the previous
			// implementation
			boolean subDirPresent = layout == CODAOSGiManger.Layout.DoubleFolded;

			loadProjectionRulesParser(bundlesRootDir, subDirPresent);
		} else {
			// A flat directory
			bundleToBeStarted.clear();
			findAndInstallPlugin(bundlesRootDir);
			startAllBundle();
		}
	}

	/**
	 * Get the map containing all the implementation of the Projection Rule
	 * Parser loaded by Felix
	 * 
	 * @return the map containing all the implementation of the Projection Rule
	 *         Parser
	 */
	public HashMap<String, ParserPR> getProjectionRuleParserMap() {
		HashMap<String, ParserPR> projectionRuleParserMap = new HashMap<String, ParserPR>();

		ArrayList<ParserPR> prpList = getServletExtensionsForType(ParserPR.class);

		for (ParserPR projRuleParse : prpList) {
			projectionRuleParserMap.put(projRuleParse.getId(), projRuleParse);
		}
		return projectionRuleParserMap;
	}

	/**
	 * Get the map containing all the implementation of the Projection
	 * Disambiguation Annotation loaded by Felix
	 * 
	 * @return the map containing all the implementation of the Projection
	 *         Disambiguation Annotation
	 */
	public HashMap<String, ProjectionRulesDeciderInterface> getProjectonRulesDeciderMap() {
		HashMap<String, ProjectionRulesDeciderInterface> projectionDisambiguatioAnnotationnMap = new HashMap<String, ProjectionRulesDeciderInterface>();

		ArrayList<ProjectionRulesDeciderInterface> pdaList = getServletExtensionsForType(ProjectionRulesDeciderInterface.class);

		for (ProjectionRulesDeciderInterface projectionDisambiguation : pdaList) {
			projectionDisambiguatioAnnotationnMap.put(
					projectionDisambiguation.getId(), projectionDisambiguation);
		}
		return projectionDisambiguatioAnnotationnMap;
	}

	/**
	 * Get the map containing all the implementation of the Identity Resolution
	 * Annotation loaded by Felix
	 * 
	 * @return the map containing all the implementation of the Identity
	 *         Resolution Annotation
	 */
	public HashMap<String, IdentityResolverInterface> getIdentityResolutionMap() {
		HashMap<String, IdentityResolverInterface> identityResolutionAnnotationMap = new HashMap<String, IdentityResolverInterface>();

		ArrayList<IdentityResolverInterface> irList = getServletExtensionsForType(IdentityResolverInterface.class);

		for (IdentityResolverInterface identityResolutionAnnotation : irList) {
			identityResolutionAnnotationMap.put(
					identityResolutionAnnotation.getId(),
					identityResolutionAnnotation);
		}
		return identityResolutionAnnotationMap;
	}

	public Felix getFramework() {
		return m_felix;
	}
}
